package com.techshinogongjian.fp.comm;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;

import com.techshinogongjian.fp.util.FpCoder;
import com.techshinogongjian.fp.util.FpConst;
import com.techshinogongjian.fp.util.FpImage;
import com.techshinogongjian.fp.util.FpProtocol;
import com.techshinogongjian.fp.util.Logs;

import android.graphics.Bitmap;
import android.graphics.BitmapFactory;

/**
 * 蓝牙指纹协议层
 * 
 * @author wangzhi
 */
public class BluetoothComm extends IComm {

	private static final String TAG = BluetoothComm.class.getSimpleName();
	private static final int IMG_SIZE = 192 * 192;

	// 协议层
	private int BYTE_EACH_TIME = 4096;
	private byte[] BUFFER_OUTPUT = new byte[BYTE_EACH_TIME / 2];
	private byte[] IMAGE_DATA = new byte[IMG_SIZE];
	private byte[] IMAGE_DATA_WITH_HEAD = new byte[BYTE_EACH_TIME + 12];
	private FpImage mSaveRawAsBmpBuf = new FpImage();
	
	private boolean bIsOpened = false;

	// private int nCmdDataFormat = FpConst.LSB_SPT;
	private int nCmdDataFormat = FpConst.LSB_BIN;

	private int nCmdHeadChar = FpConst.COM_HDR;
	private FpProtocol nCmdProtocol = FpProtocol.BXY_LASERNEW;

	// 命令行发送缓冲区
	private byte[] bSendBuf = null;
	private int nSendDatLen = 0;
	// 接收缓冲区
	private byte[] bRecvBuf = null;
	private int nRecvDatLen = 0;
	private int nCmdAckVal = 0;

	
	InputStream mInputStream;
	OutputStream mOutputStream;

	private boolean isBusy = false;

	public BluetoothComm(InputStream in, OutputStream out) {
		mInputStream = in;
		mOutputStream = out;
	}

	public boolean isOpened() {
		return true;
	}
	
	public boolean isBusy() {
		return isBusy;
	}
	
	public void setBusy(boolean isBusy) {
		this.isBusy = isBusy;
	}

	public boolean isConnected() {
		final byte[] buffer = { 0x7e, 0x62, 0x00, 0x00, 0x00, 0x00, 0x00, 0x62 };
		final byte[] result = new byte[12];

		// InitBt(0);
		Logs.i(TAG, " Connect_detect ");
		buffer[buffer.length - 1] = (byte)(FpCoder.XorChk(buffer, 1, buffer.length-1) & 0x000000FF);

		if (write(buffer) < 0) {
			return false;
		}
		if (read(result, 0, result.length) < 0) {
			return false;
		}
		return false;
	}

	public Bitmap getFingerpintImg() {
		Logs.i(TAG, "getFingerpintImg...");
		for (int i = 0; i < IMG_SIZE / BYTE_EACH_TIME; i++) {
			if (!getImgBlock(i)) {
				Logs.e(TAG, "getImgBlock fail...");
				return null;
			}
			System.arraycopy(IMAGE_DATA_WITH_HEAD, 11, IMAGE_DATA, i
					* BYTE_EACH_TIME, BYTE_EACH_TIME);
		}
		mSaveRawAsBmpBuf.saveRawAsBmpBuf(IMAGE_DATA, 192, 192);
		return BitmapFactory.decodeByteArray(
				mSaveRawAsBmpBuf.getBmpImgBuf(), 0,
				(int) mSaveRawAsBmpBuf.getOpBmpImgLen());
	}

	private boolean getImgBlock(int pos) {
		int realReadByte = 0;
		int block_position = 0;
		byte buffer[] = { 0x7e, 0x62, 0x22, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x00, (byte) 0x10, 0x00, 0x49 }; // read image
		//System.arraycopy(FpConst.int2byte(BYTE_EACH_TIME), 0, buffer, 11, 4); // len
		//System.arraycopy(FpConst.int2byte(pos * BYTE_EACH_TIME), 0, buffer, 7, 4); // pos
		buffer[15] = (byte) (buffer[1] ^ buffer[2] ^ buffer[3] ^ buffer[4]
				^ buffer[5] ^ buffer[6] ^ buffer[7] ^ buffer[8] ^ buffer[9]
				^ buffer[10] ^ buffer[11] ^ buffer[12] ^ buffer[13] ^ buffer[14]);

		if (write(buffer) != FpConst.TCY_SUCC) {
			Logs.e(TAG, "write fail...");
			return false;
		}

		while (true) {
			realReadByte = read(BUFFER_OUTPUT, 0, BUFFER_OUTPUT.length);
			if (realReadByte < 0) {
				Logs.e(TAG, "read failed...");
				return false;
			}

			System.arraycopy(BUFFER_OUTPUT, 0, IMAGE_DATA_WITH_HEAD,
					block_position, realReadByte);
			
			block_position = block_position + realReadByte;
			if (block_position == BYTE_EACH_TIME + 12)
				break;
			else {
				// continue;?
			}
		}
		return true;
	}


	/* 蓝牙指纹交互 */
	public int lxbCmdDevInfo() {
		int nRet;
		Log("lxbCmdDevInfo");
		nSendDatLen = 0;
		nRet = doCommand(FpConst.LSB_INFO);
		Log("lxbCmdDevInfo	OK");
		return nRet;
	}

	/* 蓝牙指纹交互 */
	public int lxbCmdSubDev(int nParamId, int nVal) {
		int nRet;
		bSendBuf[0] = (byte) (nParamId & 0x000000FF);
		nSendDatLen = 1;
		nRet = doCommand(FpConst.LSB_CTRL);
		return nRet;
	}

	/* 蓝牙指纹读取Sn */
	public int lxbCmdDevSn() {
		int nRet;
		nSendDatLen = 0;
		nRet = doCommand(FpConst.LSB_RDSN);
		return nRet;
	}
	
	
	/**
	 * 发送指令
	 * 
	 * @param bCmdBytes
	 * @param nCmdSendLen
	 * @return
	 */
	private int doSendCmd(byte[] bCmdBytes, int nCmdSendLen) {
		HexOut("\ndoSendCmd", bCmdBytes, 0, nCmdSendLen);
		int nRet = 0;
		if (isOpened() == false) {
			return FpConst.TCY_NLNK;
		}
		nRet = write(bCmdBytes, 0, nCmdSendLen);
		if (nRet >= 0) {
			flush();
		}
		return nRet;
	}

	private int doRecvCmdHDR() {
		int nRet = 0;
		byte[] bRecvHead = new byte[32];
		int nRdLen = 0;
		boolean bStart = false;

		if (isOpened() == false) {
			return FpConst.TCY_NLNK;
		}
		bRecvHead[0] = 0x00;
		bRecvHead[1] = 0x00;
		// 接收请求头
		while (true) {
			nRet = read(bRecvHead, nRdLen, 1);
			if (nRet <= 0) {
				Log("doRecvCmdHDR.mComm.read nRet=" + nRet);
				if (nRet == 0) {
					nRet = FpConst.TCY_ERRO;
					Log("doRecvCmdHDR.mComm.read Cvt2 nRet=" + nRet);
				}
				break;
			} else {
				if (bRecvHead[0] == (FpConst.COM_HDR & 0x000000FF)) {
					nRet = 0;
					break;
				} else {
					Log("doRecvCmdHDR.drop data.");
				}
			}
		}
		// 正常收到头字节
		return nRet;
	}

	private int doRecvBin(byte[] bCmdRecvBin, int nStartPos, int nBinLen,
			int nCmdFmt) {
		int nRet = 0;
		int nRdLen = 0;
		int nWantLen = nBinLen;
		byte[] bRecvtmp = null;

		if (isOpened() == false) {
			return FpConst.TCY_NLNK;
		}
		if ((nCmdFmt == FpConst.LSR_SPT) || (nCmdFmt == FpConst.LSB_SPT)) {
			nWantLen = nBinLen * 2;
		}
		bRecvtmp = new byte[nWantLen];
		// 接收请求头
		while (nRdLen < nWantLen) {
			int nLeftLen = nWantLen - nRdLen;
			nRet = read(bRecvtmp, nRdLen, nLeftLen);
			if (nRet < 0) {
				Log("doRecvBin.mComm.read nRet=" + nRet);
				break;
			} else {
				nRdLen += nRet;
			}
		}

		if (nRdLen > 0)
			HexOut("doRecvBin Debug:", bRecvtmp, 0, nRdLen);
		if (nRdLen < nWantLen) {
			Log("doRecvBin Response Fail. nRdLen =" + nRdLen);
			return FpConst.TCY_ERRO;
		}
		// 解析指令
		if ((nCmdFmt == FpConst.LSR_SPT) || (nCmdFmt == FpConst.LSB_SPT)) {
			byte[] bTmpBin = FpCoder.DecodeX30(bRecvtmp, 0, nWantLen);
			if (bTmpBin.length != nBinLen) {
				if (bTmpBin.length > 0)
					HexOut("doRecvBin Debug: genX302Bin", bTmpBin, 0,
							bTmpBin.length);
				return FpConst.TCY_ERRO;
			} else {
				System.arraycopy(bTmpBin, 0, bCmdRecvBin, nStartPos,
						bTmpBin.length);
				Log("doRecvBin Response Fail. nRdLen =" + nRdLen);
			}
		} else {
			System.arraycopy(bRecvtmp, 0, bCmdRecvBin, nStartPos, nRdLen);
			nRet = nRdLen;
		}
		return nRet;
	}

	/**
	 * 接收指令响应包 LaserX协议
	 * 
	 * @param bCmdBytes
	 * @return
	 */
	private int doRecvCmd() {
		Logs.i(TAG,"doRecvCmd...");
		int nRet = 0;
		byte[] bRecvHead = new byte[32];
		int nDataLen = 0;

		nCmdAckVal = FpConst.LSE_FAIL;
		nRecvDatLen = 0;

		if (isOpened() == false) {
			return FpConst.TCY_NLNK;
		}

		// 指令头
		nRet = doRecvCmdHDR();
		if (nRet < FpConst.TCY_SUCC) {
			Log("doRecvCmd  Fail. nRet =" + nRet);
			return nRet;
		} else {
			bRecvHead[0] = (FpConst.COM_HDR & 0x000000FF);
		}
		// 指令格式
		nRet = doRecvBin(bRecvHead, 1, 1, FpConst.LSR_BIN);
		if (nRet < FpConst.TCY_SUCC) {
			Log("doRecvCmd FMT Fail. nRet =" + nRet);
			return nRet;
		} else {
			Log("doRecvCmd FMT OK. nRet =" + (int) bRecvHead[1]);
		}
		// 指令头其余部分
		nRet = doRecvBin(bRecvHead, 2, 6, nCmdDataFormat);
		if (nRet < FpConst.TCY_SUCC) {
			Log("doRecvCmd CMD/ACK/LEN Fail. nRet =" + nRet);
			return nRet;
		} else {
			// 已经都是Bin格式了
			Log("doRecvCmd. nRet =" + nRet);
			HexOut("doRecvCmd bRecvHead:", bRecvHead, 0, 8);
			// 获得长度
			nDataLen = ((bRecvHead[4] << 24) & 0xFF000000)
					| ((bRecvHead[5] << 16) & 0x00FF0000)
					| ((bRecvHead[6] << 8) & 0x0000FF00)
					| ((bRecvHead[7]) & 0x000000FF);
			// 指令状态
			nCmdAckVal = bRecvHead[3];
			Log("doRecvCmd parse nDataLen =" + nDataLen);
		}
		// 接收BODY区
		byte[] bDataChk = new byte[nDataLen + 1];
		nRet = doRecvBin(bDataChk, 0, nDataLen + 1, nCmdDataFormat);
		if (nRet < FpConst.TCY_SUCC) {
			Log("doRecvCmd Data/CHK Fail. nRet =" + nRet);
			return nRet;
		} else {
			Log("doRecvCmd Data/CHK OK. =" + nRet);
		}
		// 拼组整体响应报文区
		int nTotalLen = 1 + 1 + 1 + 1 + 4 + nDataLen + 1;
		Log("doRecvCmd sum nTotalLen =" + nTotalLen);
		byte[] bRecvALL = new byte[nTotalLen];
		System.arraycopy(bRecvHead, 0, bRecvALL, 0, 8);
		Log("bRecvHead.length:" + bRecvHead.length + " bRecvALL.length:"
				+ bRecvALL.length);
		System.arraycopy(bDataChk, 0, bRecvALL, 8, nDataLen + 1);
//		Log("bDataChk.length:" + bDataChk.length + " bDataChk:" + bDataChk[8]
//				+ " bRecvALL.length:" + bRecvALL.length);
		HexOut("ALL:", bRecvALL, 0, nTotalLen);
		// 核验校验值
		int nChk = FpCoder.XorChk(bRecvALL, 1, (3 + 4 + nDataLen));
		Logs.e(TAG, "chk & 0x0000fff:" + nChk + " "
				+ ((byte) (nChk & 0x000000FF)));
		Logs.e(TAG, "bRecvALL[nTotalLen - 1]:" + bRecvALL[nTotalLen - 1]);
		if (bRecvALL[nTotalLen - 1] != (byte) (nChk & 0x000000FF)) {
			HexOut("doRecvCmd CHK Fail. BufDebug:", bRecvALL, 0, nTotalLen);
			return FpConst.TCY_ERRO;
		} else {
			Log("doRecvCmd sum CHK OK");
			// 终于执行完了
			if (nDataLen > 0) {
				System.arraycopy(bDataChk, 0, bRecvBuf, 0, nDataLen);
				nRecvDatLen = nDataLen;
			}
		}
		Log("doRecvCmd Over.");
		nRet = CnvtErrLaserX(nCmdAckVal);
		Log("doRecvCmd Ack=" + nCmdAckVal + ", Cvt to nRet=" + nRet);
		return nRet;
	}

	/*
	 * 执行 LaserX协议命令
	 * 
	 * 指令接口与外部交互均已Bin格式进行， 命令执行过程可以选用Split x30格式
	 */
	private int doCommandBT(int nCmdId) {
		int nRet = 0;

		// HDR1 + FMT1 + CMD1 + LEN4 + DATA + CHK1
		int nSendLen = 1 + 1 + 1 + 4 + nSendDatLen + 1;
		byte[] bSend = new byte[nSendLen];

		// 蓝牙强制用这个协议
		nCmdDataFormat = FpConst.LSB_BIN;
		// 拼组Bin格式请求命令
		bSend[0] = (byte) (nCmdHeadChar & 0x000000FF);
		bSend[1] = (byte) (nCmdDataFormat & 0x000000FF);
		bSend[2] = (byte) (nCmdId & 0x000000FF);
		byte[] bLen = FpCoder.Int2Byte(nSendDatLen);
		System.arraycopy(bLen, 0, bSend, 3, 4);
		if (nSendDatLen > 0) {
			System.arraycopy(bSendBuf, 0, bSend, 7, nSendDatLen);
		}
		int nChk = FpCoder.XorChk(bSend, 1, nSendDatLen + 6);
		bSend[nSendLen - 1] = (byte) (nChk & 0x000000FF);

		HexOut("\ndoCommandBT BIN=", bSend, 0, nSendLen);

		// 发送命令请求
		nRet = doSendCmd(bSend, bSend.length);
		if (nRet >=0 ) {
			nRet = doRecvCmd();
		}
		Log("doCommandBT nRet=" + nRet);

		return nRet;
	}

	/*
	 * 执行 LaserX协议命令
	 * 
	 * 指令接口与外部交互均已Bin格式进行， 命令执行过程可以选用Split x30格式
	 */
	public int doCommand(int nCmdId) {
		int nRet = 0;
		Log("doCommand Start");

		switch (nCmdProtocol) {
		case BXY_LASERNEW:
			nRet = doCommandBT(nCmdId);
			break;
		default:
			nRet = FpConst.TCY_NSUP;
			break;
		}
		Log("\ndoCommand End nRet=" + nRet);
		return nRet;
	}

	/**
	 * 将设备的响应码转换为API的错误码
	 * 
	 * @param nDevAck
	 * @return
	 */
	private int CnvtErrLaserX(int nDevAck) {
		switch (nDevAck) {
		case FpConst.LSE_FAIL:
			return (FpConst.TCY_FAIL); // 失败结果
		case FpConst.LSE_ERRO:
			return (FpConst.TCY_ERRO); // 校验错误
		case FpConst.LSE_TMOT:
			return (FpConst.TCY_TMOT); // 操作超时
		case FpConst.LSE_NODV:
			return (FpConst.TCY_NODV); // 未连设备
		case FpConst.LSE_FLSH:
			return (FpConst.TCY_FLSH); // 写闪存错
		case FpConst.LSE_NOFP:
			return (FpConst.TCY_NOFP); // 未按或孬
		case FpConst.LSE_NOSM:
			return (FpConst.TCY_NSAM); // 值不相关
		case FpConst.LSE_NOMT:
			return (FpConst.TCY_NMAT); // 值不匹配
		case FpConst.LSE_EPLB:
			return (FpConst.TCY_EMPT); // 库链为空
		case FpConst.LSE_TOLV:
			return (FpConst.TCY_TOLV); // 请抬起手
		case FpConst.LSE_NMEM:
			return (FpConst.TCY_NMEM); // 内存不足
		case FpConst.LSE_NSUP:
			return (FpConst.TCY_NSUP); // 不支持令
		case FpConst.LSE_LESS:
			return (FpConst.TCY_LESS); // 特点过少
		case FpConst.LSE_CNCL:
			return (FpConst.TCY_CNCL); // 取消操作
		case FpConst.LSE_BUSY:
			return (FpConst.TCY_BUSY); // 我很忙啊
		default: {
			if (nDevAck > 0)
				return -nDevAck;
			return nDevAck;
			// return (FpConst.TCY_NSUP);
		}
		}
	}

	
	@Override
	public int read(byte[] pbReadOutBuf, int nStartPos, int nWantReadLen) {
		int nRet = FpConst.TCY_NLNK;
		try {
			nRet = mInputStream.read(pbReadOutBuf, nStartPos, nWantReadLen);
		} catch (IOException e) {
			e.printStackTrace();
			return nRet;
		}
		return nRet;
	}

	@Override
	public int write(byte[] data) {
		try {
			mOutputStream.write(data);
		} catch (IOException e) {
			e.printStackTrace();
			return FpConst.TCY_NLNK;
		}
		return FpConst.TCY_SUCC;
	}

	public int write(byte[] pbWriteBuf, int nStartPos, int nWantWriteLen) {
		if (isOpened() == false)
			return FpConst.TCY_NLNK;
		int nRet = 0;
		try {
			mOutputStream.write(pbWriteBuf, nStartPos, nWantWriteLen);
			nRet = nWantWriteLen;
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
			nRet = FpConst.TCY_NLNK;
		}
		return nRet;
	}

	public int flush() {
		if (isOpened() == false)
			return FpConst.TCY_NLNK;
		try {
			mOutputStream.flush();
		} catch (IOException e) {
			// TODO Auto-generated catch block
			e.printStackTrace();
		}
		return 0;
	}

	
	private static void Log(String sMsg) {
		Logs.i(TAG, sMsg);
	}
	private static void HexOut(String tag, byte[] bOut, int nOffSet, int nLen){
		Logs.HexOut(tag, bOut, nOffSet, nLen);
	}

	@Override
	public boolean isCancel() {
		// TODO Auto-generated method stub
		return false;
	}
	
}
